package com.gzsxy.esjy.mapping;


import com.gzsxy.esjy.common.Assert;
import com.gzsxy.esjy.mapping.annotation.Param;
import com.gzsxy.esjy.mapping.annotation.PathVariable;
import com.gzsxy.esjy.mapping.annotation.RequestBody;
import com.gzsxy.esjy.mapping.annotation.RequestMapping;
import com.gzsxy.esjy.mapping.annotation.enums.RequestMethod;
import io.netty.handler.codec.http.HttpMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 映射初始化
 * @author zhangxiaolong
 * @date 2022/7/6 9:36
 */
public class MappingInit {

	private static final Map<String,MappingEntity> MAPPING_ENTITYS = new HashMap<String, MappingEntity>(16);
	/**
	 * 路径分隔符
	 */
	private static final String PATN_SEPARATOR = "/";

	private static Logger log = LoggerFactory.getLogger(MappingInit.class) ;
    
	/** 全量路径匹配*/
	private final static Set<String> FULL_PATHS = new HashSet<String>();
	
	/** restful风格缺省匹配()*/
	private final static List<String> RESTFUL_PATHS = new ArrayList<String>() ;
	
	/** path匹配*/
	private final static CoustomAntPathMatcher ANT_PATH_MATCHER = new CoustomAntPathMatcher();
	
	/** 判断是否restfull [^\}] 匹配不为}的所有字符     path=/file/{id}  */
	private final static Pattern PATTERN = Pattern.compile("(\\{[^\\}]+})");
	
	/** 抽出泛型*/
	private final static Pattern GENERICITY_TYPE = Pattern.compile("(\\<[^\\}]+>)");

	/** /S匹配任何非空白字符。等等
	 * 匹配${xxx} 的string    ${server.http.keep_live:true}*/
	private final static Pattern MAPPING_PATTERN = Pattern.compile("\\$\\{\\S+\\}");
	/** /s*匹配0个或多个空格，会尽可能多的匹配   ;  匹配    xx:xx */
	private final static Pattern MAPPING_DEFAULT = Pattern.compile("\\s*[:]+\\s*");;
	
	/**
	 * 读取方法参数名称
	 */
	private ParameterNameDiscoverer parameterNameDiscoverer = 
			new LocalVariableTableParameterNameDiscoverer();

	/**
	 * 生效环境变量
	 */
	@Autowired
	private Environment environment ;
	
	public MappingInit() {
		log.info("开始封装映射路径。。。。");
	}

	/**
	 *装配路径
	 */
	public void addMapping(String beanName,Object obj) throws Exception {
		Class<? extends Object> clazz =  obj.getClass() ;
		RequestMapping annotation = null ;
		MappingEntity entity = null ;
		String path = "";
		annotation = clazz.getAnnotation(RequestMapping.class) ;
		if(annotation != null){
			path = getMappingPath(annotation.value()) ;
			path = this.removeLast(path, PATN_SEPARATOR) ;
		}
		//获取所有公有方法
		Method[] methods = clazz.getMethods() ;
		String mPath ;
		for (Method method : methods) {
			annotation = method.getAnnotation(RequestMapping.class) ;
			if(annotation == null){
				continue ;
			}
			//如果为路径直接返回，如果为${server.path:/ceshi} 则生效默认值
			mPath = getMappingPath(annotation.value());
			this.addFirst(mPath, PATN_SEPARATOR);
			if(MAPPING_ENTITYS.containsKey(path+mPath)){
				log.warn("repeat Mapping method-{} {} path-{} is exit",clazz.getName(),method.getName(),path+mPath);
				continue ;
			}
			entity = new MappingEntity() ;
			RequestMethod[] supportMethods = annotation.method();
			//处理支持的请求方式
			dealSupportMotheds(entity,supportMethods);
			entity.setClazz(obj.getClass());
			entity.setClazzPath(path);
			entity.setMethodName(method.getName());
			entity.setArgs(method.getParameterTypes());
			//处理泛型参数
			dealGenericityArgs(method,entity);
			log.info("Mapped {}||{}",path+mPath,clazz.getName()+" "+method.getName());
			//获取参数方法名称
			String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
			entity.setParamNames(parameterNames);
			//处理参数中的注解
			dealWidthParameterNames(parameterNames,method.getParameterAnnotations(),clazz.getName()+" "+method.getName(),entity) ;
			String traPath = path+mPath;
			MAPPING_ENTITYS.put(traPath, entity);
			
			if(isRestfulPath(traPath)){
				//进行相同匹配,进行pattenPath缓存
				ANT_PATH_MATCHER.match(traPath, traPath);
				RESTFUL_PATHS.add(traPath);
			}else{
				FULL_PATHS.add(traPath);
			}
			
			
		}
	}
	
	/**
	 * 处理支持的请求方式
	 * @param entity
	 * @param supportMethods
	 */
	private void dealSupportMotheds(MappingEntity entity, RequestMethod[] supportMethods) {
		List<HttpMethod> list = new ArrayList<HttpMethod>() ;
		for (RequestMethod httpMethod : supportMethods) {
			String name = httpMethod.name();
			HttpMethod valueOf = HttpMethod.valueOf(name);
			if(valueOf != null) {
				list.add(valueOf);
			}
		}
		entity.setSupportMotheds(list);
	}

	/**
	 * 处理泛型参数
	 * @param method
	 * @param entity
	 * @throws ClassNotFoundException
	 */
	private void dealGenericityArgs(Method method, MappingEntity entity) throws ClassNotFoundException {
        //方法返回一个Type对象的数组,它以声明顺序表示此Method对象表示的方法的形式参数类型。
		Type[] genericParameterTypes = method.getGenericParameterTypes();
		//它们以声明顺序表示由此Method对象表示的方法的形式参数类型。如果底层方法没有参数，则返回长度为0的数组。
		Class<?>[] parameterTypes = method.getParameterTypes();
		Class<?>[] argsExs = new Class<?>[parameterTypes.length] ;
		for (int i=0; i< parameterTypes.length; i++) {
			String type = genericParameterTypes[i].toString();
			if(!type.startsWith("class")){
				Matcher matcher = GENERICITY_TYPE.matcher(type);
				//匹配存在
				if(matcher.find()){
					//可以将所有匹配项全部提取出来
					String group = matcher.group();
					Class<?> forName = Class.forName(group.substring(1, group.length()-1)) ;
					argsExs[i] = forName;
				}
			}
			if(argsExs[i] == null){
				argsExs[i] = parameterTypes[i];
			}
			
		}
		entity.setArgsEx(argsExs);
	}

	/**
	 * 处理参数中的注解
	 * @param parameterNames
	 * @param parameterAnnotations
	 * @param tag
	 */
	private void dealWidthParameterNames(String[] parameterNames,Annotation[][] parameterAnnotations,String tag,MappingEntity entity){
		for(int i = 0 ; i < parameterNames.length ;i++){
			Annotation[] anno = parameterAnnotations[i] ;
			for (Annotation annotation : anno) {
				if(annotation.annotationType() == RequestBody.class){
					RequestBody resquestBody = (RequestBody) annotation ;
					String value = resquestBody.value() ;
					if(!"".equals(value)){
						parameterNames[i] = value ;
					}
					entity.setBodyName(parameterNames[i]);
				}
				
				if(annotation.annotationType() == Param.class) {
					Param param = (Param)annotation;
					String value = param.value();
					if(!"".equals(value)){
						parameterNames[i] = value ;
					}
				}
				
				if(annotation.annotationType() == PathVariable.class) {
					PathVariable param = (PathVariable)annotation;
					String value = param.value();
					if(!"".equals(value)){
						parameterNames[i] = value ;
					}
				}
			}
		}
	}
	
	/**
	 * 移除字符串后面的指定字符
	 * @param url
	 * @param remove
	 * @return
	 */
	private  String removeLast(String url,String remove){
		while(url.endsWith(remove)){
			url = url.substring(0,url.lastIndexOf(remove)) ;
		}
		return url ;
	}
	/**
	 * 移除字符串后面的指定字符
	 * @param url
	 * @param add
	 * @return
	 */
	private  String addFirst(String url,String add){
		if(!url.startsWith(add)){
			url = add+url ;
		}
		return url ;
	}
	
	/**
	 * 转换路径
	 * @param path
	 * @return
	 */
	public static String checkPath(String path,Map<String, Object> variables){
		if(variables == null){
			variables = new LinkedHashMap<String, Object>();
		}
		if(!StringUtils.isEmpty(path)){
			//判断是否全量匹配
			if(FULL_PATHS.contains(path)){
				return path;
			}
			//缺省匹配
			for (String restfulPath : RESTFUL_PATHS) {
				if(ANT_PATH_MATCHER.coustomMatch(restfulPath,path,variables)){
					return restfulPath;
				}
			}
			log.error("the path={} is not found ..",path);
			return null;
		}
		log.error("path is null ..");
		return null;
	}

	/**
	 * 判断是否resful接口
	 * @param path
	 * @return
	 */
    private boolean isRestfulPath(String path){
    	Matcher matcher = PATTERN.matcher(path);
    	return matcher.find();
    }
	
    public MappingEntity getMapping(String path){
    	return MAPPING_ENTITYS.get(path);
    }
    
    /**
	 * 如果为路径直接返回，如果为${server.path:/ceshi} 则生效默认值
	 * @param value
	 * @return
	 */
	private String getMappingPath(String value) {
		Matcher matcher = MAPPING_PATTERN.matcher(value);
		String key = null;
		//如果匹配成功
		if(matcher.find()) {
			//可以将所有匹配项全部提取出来
			key = matcher.group() ;
			key = key.substring(2, key.length() -1) ;
		}else {
			return value ;
		}
		String[] split = MAPPING_DEFAULT.split(key);
		String defaultValue = null;
		//取出默认值  ${server.http.keep_live:true}
		if(split.length >1) {
			defaultValue = split[1] ;
		}
		key = split[0] ;
		//生效环境变量 ${server.http.keep_live:true}
		String property = environment.getProperty(key, defaultValue);
		Assert.isNotNull(property, "property[\""+key+"\"] is null");
		return property ;
	}
}
